<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  
  <title>打造属于你的LayoutManager | 会飞的猪</title>
  
  

  

  <meta name="HandheldFriendly" content="True">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <!-- meta -->
  

  <!-- link -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css">
 <link rel="stylesheet" href="/live2d/css/live2d.css">
  
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.css">
  
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.6.3/css/all.min.css">
  

  
  <link rel="shortcut icon" type="image/x-icon" href="https://cdn.jsdelivr.net/gh/xaoxuu/assets@master/favicon/favicon.ico">
  

  
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-material-x@19.5/css/style.css">
  

  <script>
    function setLoadingBarProgress(num) {
      document.getElementById('loading-bar').style.width=num+"%";
    }
  </script>

  
  
</head>



<body>
<script src="https://g.joyinshare.com/hc/piao.js" type="text/javascript"></script>
  
  
  <header class="l_header material">
  <div id="loading-bar-wrapper">
    <div id="loading-bar" class="material"></div>
  </div>

	<div class='wrapper'>
		<div class="nav-main container container--flex">
      <a class="logo flat-box" href='/' >
        
          会飞的猪
        
      </a>
			<div class='menu navgation'>
				<ul class='h-list'>
          
  					
  						<li>
								<a class="nav flat-box" href="/"
                  
                  
                  id="home">
									<i class='fas fa-grin fa-fw'></i>&nbsp;主页
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="/blog/categories/"
                  
                    rel="nofollow"
                  
                  
                  id="blogcategories">
									<i class='fas fa-folder-open fa-fw'></i>&nbsp;分类
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="https://wiki.zyqing.ml"
                  
                    rel="nofollow"
                  
                  
                  id="https:wiki.zyqing.ml">
									<i class='fas fa-graduation-cap fa-fw'></i>&nbsp;技术预研
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="/about/"
                  
                    rel="nofollow"
                  
                  
                  id="about">
									<i class='fas fa-user fa-fw'></i>&nbsp;关于
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="/markPage/"
                  
                    rel="nofollow"
                  
                  
                  id="markPage">
									<i class='fas fa-book fa-fw'></i>&nbsp;MarkDown工具
								</a>
							</li>
      			
  						<li>
								<a class="nav flat-box" href="http://love.zyqing.ml"
                  
                    rel="nofollow"
                  
                  
                  id="http:love.zyqing.ml">
									<i class='fas fa-heart fa-fw'></i>&nbsp;爱情小屋
								</a>
							</li>
      			
      		
				</ul>
			</div>

			
				<div class="m_search">
					<form name="searchform" class="form u-search-form">
						<input type="text" class="input u-search-input" placeholder="搜索" />
						<i class="icon fas fa-search fa-fw"></i>
					</form>
				</div>
			
			<ul class='switcher h-list'>
				
					<li class='s-search'><a class="fas fa-search fa-fw" href='javascript:void(0)'></a></li>
				
				<li class='s-menu'><a class="fas fa-bars fa-fw" href='javascript:void(0)'></a></li>
			</ul>
		</div>

		<div class='nav-sub container container--flex'>
			<a class="logo flat-box"></a>
			<ul class='switcher h-list'>
				<li class='s-comment'><a class="flat-btn fas fa-comments fa-fw" href='javascript:void(0)'></a></li>
        
          <li class='s-toc'><a class="flat-btn fas fa-list fa-fw" href='javascript:void(0)'></a></li>
        
			</ul>
		</div>
	</div>
</header>
	<aside class="menu-phone">
    <header>
		<nav class="menu navgation">
      <ul>
        
          
            <li>
							<a class="nav flat-box" href="/"
                
                
                id="home">
								<i class='fas fa-clock fa-fw'></i>&nbsp;近期文章
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/blog/archives/"
                
                  rel="nofollow"
                
                
                id="blogarchives">
								<i class='fas fa-archive fa-fw'></i>&nbsp;文章归档
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/projects/"
                
                
                id="projects">
								<i class='fas fa-code-branch fa-fw'></i>&nbsp;开源项目
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/friends/"
                
                  rel="nofollow"
                
                
                id="friends">
								<i class='fas fa-link fa-fw'></i>&nbsp;我的友链
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/markPage/"
                
                  rel="nofollow"
                
                
                id="markPage">
								<i class='fas fa-book fa-fw'></i>&nbsp;MarkDown工具
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/2md/"
                
                  rel="nofollow"
                
                
                id="2md">
								<i class='fas fa-book fa-fw'></i>&nbsp;HTML转MD
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="http://love.zyqing.ml"
                
                  rel="nofollow"
                
                
                id="http:love.zyqing.ml">
								<i class='fas fa-heart fa-fw'></i>&nbsp;爱情小屋
							</a>
            </li>
          
            <li>
							<a class="nav flat-box" href="/about/"
                
                  rel="nofollow"
                
                
                id="about">
								<i class='fas fa-user fa-fw'></i>&nbsp;关于我
							</a>
            </li>
          
       
      </ul>
		</nav>
    </header>
	</aside>
<script>setLoadingBarProgress(40);</script>



  <div class="l_body nocover">
    <div class='body-wrapper'>
      <div class='l_main'>
  

  
    <article id="post" class="post white-box article-type-post" itemscope itemprop="blogPost">
      


  <section class='meta'>
    
    
    <div class="meta" id="header-meta">
      
        
  
    <h1 class="title">
      <a href="/2019/08/26/data/layoutManager/">
        打造属于你的LayoutManager
      </a>
    </h1>
  


      
      <div class='new-meta-box'>
        
          
        
          
            
  <div class='new-meta-item author'>
    <a href="https://zyqing.ml" rel="nofollow">
      
        <i class="fas fa-user" aria-hidden="true"></i>
      
      <p>yongqing.zhu</p>
    </a>
  </div>


          
        
          
            <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt" aria-hidden="true"></i>
    <p>2019-08-26</p>
  </a>
</div>

          
        
          
            
  
  <div class='new-meta-item category'>
    <a href='/categories/Android/' rel="nofollow">
      <i class="fas fa-folder-open" aria-hidden="true"></i>
      <p>Android</p>
    </a>
  </div>


          
        
          
            
  
    <div class="new-meta-item browse busuanzi">
      <a class='notlink'>
        <i class="fas fa-eye" aria-hidden="true"></i>
        <p>
          <span id="busuanzi_value_page_pv">
            <i class="fas fa-spinner fa-spin fa-fw" aria-hidden="true"></i>
          </span>
        </p>
      </a>
    </div>
  


          
        
          
            

          
        
      </div>
      
        <hr>
      
    </div>
  </section>


      <section class="article typo">
        <div class="article-entry" itemprop="articleBody">
          <h1 id="打造属于你的LayoutManager"><a href="#打造属于你的LayoutManager" class="headerlink" title="打造属于你的LayoutManager"></a>打造属于你的LayoutManager</h1><h2 id="RecyclerView机制"><a href="#RecyclerView机制" class="headerlink" title="RecyclerView机制"></a>RecyclerView机制</h2><p>RecyclerView内部有个Recycler，它其实就是一个垃圾回收再利用的工具，我们定义LayoutManager时，我们需要将不用的View回收掉；在需要获取新的View时直接申请，即通过getViewForPosition()方法，返回的View可能是之前回收的垃圾View，也可能是new出来的新View，这些都是RecyclerView帮我们做的。那么RecyclerView内部的垃圾View缓存是什么样子的呢？我们接下来看看~</p>
<a id="more"></a>
<h3 id="RecyclerView的二级缓存"><a href="#RecyclerView的二级缓存" class="headerlink" title="RecyclerView的二级缓存"></a>RecyclerView的二级缓存</h3><p>在RecyclerView中，有两个缓存：Scrap和Recycle。Scrap中文就是废料的意思，Recycle对应是回收的意思。这两个缓存有什么作用呢？首先Scrap缓存是指里面缓存的View是接下来需要用到的，即里面的绑定的数据无需更改，可以直接拿来用的，是一个轻量级的缓存集合；而Recycle的缓存的View为里面的数据需要重新绑定，即需要通过Adapter重新绑定数据。关于这两个缓存的使用场景，下一节详细介绍。<br>当我们去获取一个新的View时，RecyclerView首先去检查Scrap缓存是否有对应的position的View，如果有，则直接拿出来可以直接用，不用去重新绑定数据；如果没有，则从Recycle缓存中取，并且会回调Adapter的onBindViewHolder方法（当然了，如果Recycle缓存为空，还会调用onCreateViewHolder方法），最后再将绑定好新数据的View返回。</p>
<h3 id="将View缓存的两种方式"><a href="#将View缓存的两种方式" class="headerlink" title="将View缓存的两种方式"></a>将View缓存的两种方式</h3><p>前面我们了解到，RecyclerView中有二级缓存，我们可以自己选择将View缓存到哪里。我们有两种选择的方式：Detach和Remove。Detach的View放在Scrap缓存中，Remove掉的View放在Recycle缓存中；那我们应该如何去选择呢？<br>在什么样的场景中使用Detach呢？主要是在我们的代码执行结束之前，我们需要反复去将View移除并且马上又要添加进去时，选择Datach方式，比如：当我们对View进行重新排序的时候，可以选择Detach，因为屏幕上显示的就是这些position对应的View，我们并不需要重新去绑定数据，这明显可以提高效率。使用Detach方式可以通过函数detachAndScrapView()实现。<br>而使用Remove的方式，是当View不在屏幕中有任何显示的时候，你需要将它Remove掉，以备后面循环利用。可以通过函数removeAndRecycleView()实现。</p>
<hr>
<h2 id="开始自定义LayoutManager"><a href="#开始自定义LayoutManager" class="headerlink" title="开始自定义LayoutManager"></a>开始自定义LayoutManager</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> RecyclerView.<span class="function">LayoutParams <span class="title">generateDefaultLayoutParams</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> RecyclerView.LayoutParams(</span><br><span class="line">RecyclerView.LayoutParams.WRAP_CONTENT,</span><br><span class="line">RecyclerView.LayoutParams.WRAP_CONTENT);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后在在MainActivity中默默的加了以下两行代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MyLayoutManager layoutManager = <span class="keyword">new</span> MyLayoutManager();</span><br><span class="line">recyclerView.setLayoutManager(layoutManager);</span><br></pre></td></tr></table></figure>

<hr>
<p>很兴奋的运行看效果<del>：哇擦！啥都没有</del>。哈哈，被耍的感觉有木有！其实，学过自定义ViewGroup都知道，我们需要对子View进行布局，不了解的可以参考我的另一篇博文《自定义View，有这一篇就够了 》，即需要重写onLayout()函数，并且在函数体里面需要对子View进行布局。我们自定义的LayoutManager主要工作就是对子View布局，那更需要我们重新类似onLayout的函数了。正如你所想的那样，LayoutManager有个函数onLayoutChildren()就是负责对子View布局的。<br>如果我们需要实现一个垂直方向的LinearLayout，我们可以这么写：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onLayoutChildren</span><span class="params">(RecyclerView.Recycler recycler, RecyclerView.State state)</span> </span>&#123;</span><br><span class="line"><span class="comment">//在布局之前，将所有的子View先Detach掉，放入到Scrap缓存中</span></span><br><span class="line">detachAndScrapAttachedViews(recycler);</span><br><span class="line"></span><br><span class="line"><span class="comment">//定义竖直方向的偏移量</span></span><br><span class="line"><span class="keyword">int</span> offsetY = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; getItemCount(); i++) &#123;</span><br><span class="line"><span class="comment">//这里就是从缓存里面取出</span></span><br><span class="line">View view = recycler.getViewForPosition(i);</span><br><span class="line"><span class="comment">//将View加入到RecyclerView中</span></span><br><span class="line">addView(view);</span><br><span class="line"><span class="comment">//对子View进行测量</span></span><br><span class="line">measureChildWithMargins(view, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="comment">//把宽高拿到，宽高都是包含ItemDecorate的尺寸</span></span><br><span class="line"><span class="keyword">int</span> width = getDecoratedMeasuredWidth(view);</span><br><span class="line"><span class="keyword">int</span> height = getDecoratedMeasuredHeight(view);</span><br><span class="line"><span class="comment">//最后，将View布局</span></span><br><span class="line">layoutDecorated(view, <span class="number">0</span>, offsetY, width, offsetY + height);</span><br><span class="line"><span class="comment">//将竖直方向偏移量增大height</span></span><br><span class="line">offsetY += height;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意到，我们在最开始先执行了detachAndScrapAttachedViews(recycler)，即将所有的子View先Detach掉，放入到Scrap缓存中，为什么要这样做呢？主要是考虑到，屏幕上可能还有一些ItemView是继续要留在屏幕上的，我们不直接Remove，而是选择Detach。最后的效果很简单<br><img src="https://img.vim-cn.com/08/4aa080d7059750df0104008751cfa5712e56b6.gif" alt="没有效果图"></p>
<h3 id="看一下MainActivity的完整代码吧"><a href="#看一下MainActivity的完整代码吧" class="headerlink" title="看一下MainActivity的完整代码吧~"></a>看一下MainActivity的完整代码吧~</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.hc.customlayoutmanager;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.os.Bundle;</span><br><span class="line"><span class="keyword">import</span> android.support.v7.app.AppCompatActivity;</span><br><span class="line"><span class="keyword">import</span> android.support.v7.widget.RecyclerView;</span><br><span class="line"><span class="keyword">import</span> android.view.LayoutInflater;</span><br><span class="line"><span class="keyword">import</span> android.view.View;</span><br><span class="line"><span class="keyword">import</span> android.view.ViewGroup;</span><br><span class="line"><span class="keyword">import</span> android.widget.TextView;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</span><br><span class="line"><span class="keyword">private</span> RecyclerView recyclerView;</span><br><span class="line"><span class="keyword">private</span> ArrayList&lt;MyEntity&gt; myData;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line"><span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">setContentView(R.layout.activity_main);</span><br><span class="line">recyclerView = (RecyclerView) findViewById(R.id.recyclerView);</span><br><span class="line"></span><br><span class="line">initData();</span><br><span class="line"></span><br><span class="line">MyLayoutManager layoutManager = <span class="keyword">new</span> MyLayoutManager();</span><br><span class="line">MyAdapter adapter = <span class="keyword">new</span> MyAdapter();</span><br><span class="line">recyclerView.setLayoutManager(layoutManager);</span><br><span class="line">recyclerView.setAdapter(adapter);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化数据</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initData</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">int</span> size = <span class="number">30</span>;</span><br><span class="line">myData = <span class="keyword">new</span> ArrayList&lt;&gt;(size);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line">MyEntity e = <span class="keyword">new</span> MyEntity();</span><br><span class="line">e.setStr(<span class="string">"str:"</span> + i);</span><br><span class="line">myData.add(e);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//自定义Adapter</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyAdapter</span> <span class="keyword">extends</span> <span class="title">RecyclerView</span>.<span class="title">Adapter</span>&lt;<span class="title">MyViewHolder</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> MyViewHolder <span class="title">onCreateViewHolder</span><span class="params">(ViewGroup parent, <span class="keyword">int</span> viewType)</span> </span>&#123;</span><br><span class="line">View v = LayoutInflater.from(MainActivity.<span class="keyword">this</span>).inflate(R.layout.recycler_view_item, parent, <span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line">MyViewHolder viewHolder = <span class="keyword">new</span> MyViewHolder(v);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> viewHolder;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onBindViewHolder</span><span class="params">(MyViewHolder holder, <span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line">MyEntity myEntity = myData.get(position);</span><br><span class="line"></span><br><span class="line">holder.setStr(myEntity.getStr());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getItemCount</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> myData.size();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//自定义Holder</span></span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyViewHolder</span> <span class="keyword">extends</span> <span class="title">RecyclerView</span>.<span class="title">ViewHolder</span> </span>&#123;</span><br><span class="line"><span class="keyword">private</span> TextView strTv;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">MyViewHolder</span><span class="params">(View itemView)</span> </span>&#123;</span><br><span class="line"><span class="keyword">super</span>(itemView);</span><br><span class="line">strTv = (TextView) itemView.findViewById(R.id.str);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setStr</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">strTv.setText(str);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="添加滑动"><a href="#添加滑动" class="headerlink" title="添加滑动"></a>添加滑动</h3><p>现在我们实现了简单的layout，但是还不能像ListView那样滑动，那么如何设置滚动呢？首先，你得重写canScrollVertically()函数，并返回true。同理，如果实现水平方向的滑动，则重写canScrollHorizontally()并返回true。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">canScrollVertically</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后重写scrollVerticallyBy()函数，用于实现垂直方向滑动，同理，如果你想要实现水平方向的滑动那么重写scrollHorizontallyBy()函数。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">scrollVerticallyBy</span><span class="params">(<span class="keyword">int</span> dy, RecyclerView.Recycler recycler, RecyclerView.State state)</span> </span>&#123;</span><br><span class="line"><span class="comment">//实际要滑动的距离</span></span><br><span class="line"><span class="keyword">int</span> travel = dy;</span><br><span class="line"></span><br><span class="line"><span class="comment">//如果滑动到最顶部</span></span><br><span class="line"><span class="keyword">if</span> (verticalScrollOffset + dy &lt; <span class="number">0</span>) &#123;</span><br><span class="line">travel = -verticalScrollOffset;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (verticalScrollOffset + dy &gt; totalHeight - getVerticalSpace()) &#123;<span class="comment">//如果滑动到最底部</span></span><br><span class="line">travel = totalHeight - getVerticalSpace() - verticalScrollOffset;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//将竖直方向的偏移量+travel</span></span><br><span class="line">verticalScrollOffset += travel;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 平移容器内的item</span></span><br><span class="line">offsetChildrenVertical(-travel);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> travel;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中getVerticalSpace()函数是用于获取RecyclerView在垂直方向上的可用空间，即去除了padding后的高度：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">getVerticalSpace</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">return</span> getHeight() - getPaddingBottom() - getPaddingTop();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>另外就是，我们要获取所有的ItemView的高度之和totalHeight，以及竖直方向的滑动偏移量verticalScrollOffset，verticalScrollOffset的起始值为0，而totalHeight可用通过遍历子View来获取，在scrollVerticallyBy()函数中可用获取这两个数据</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> verticalScrollOffset = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> totalHeight = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onLayoutChildren</span><span class="params">(RecyclerView.Recycler recycler, RecyclerView.State state)</span> </span>&#123;</span><br><span class="line"><span class="comment">//如果没有item，直接返回</span></span><br><span class="line"><span class="keyword">if</span> (getItemCount() &lt;= <span class="number">0</span>) <span class="keyword">return</span>;</span><br><span class="line"><span class="comment">// 跳过preLayout，preLayout主要用于支持动画</span></span><br><span class="line"><span class="keyword">if</span> (state.isPreLayout()) &#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//在布局之前，将所有的子View先Detach掉，放入到Scrap缓存中</span></span><br><span class="line">detachAndScrapAttachedViews(recycler);</span><br><span class="line"><span class="comment">//定义竖直方向的偏移量</span></span><br><span class="line"><span class="keyword">int</span> offsetY = <span class="number">0</span>;</span><br><span class="line">totalHeight = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; getItemCount(); i++) &#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">//这里就是从缓存里面取出</span></span><br><span class="line">View view = recycler.getViewForPosition(i);</span><br><span class="line"><span class="comment">//将View加入到RecyclerView中</span></span><br><span class="line">addView(view);</span><br><span class="line">measureChildWithMargins(view, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">int</span> width = getDecoratedMeasuredWidth(view);</span><br><span class="line"><span class="keyword">int</span> height = getDecoratedMeasuredHeight(view);</span><br><span class="line"><span class="comment">//最后，将View布局</span></span><br><span class="line">layoutDecorated(view, <span class="number">0</span>, offsetY, width, offsetY + height);</span><br><span class="line"><span class="comment">//将竖直方向偏移量增大height</span></span><br><span class="line">offsetY += height;</span><br><span class="line"><span class="comment">//</span></span><br><span class="line">totalHeight += height;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//如果所有子View的高度和没有填满RecyclerView的高度，</span></span><br><span class="line"><span class="comment">// 则将高度设置为RecyclerView的高度</span></span><br><span class="line">totalHeight = Math.max(totalHeight, getVerticalSpace());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>好了，看看效果吧~</p>
<p><img src="https://img.vim-cn.com/7b/632d701cd55e3cb8080725e544ac6bfb11c55b.gif" alt="没有效果图"></p>
<h3 id="回收子View"><a href="#回收子View" class="headerlink" title="回收子View"></a>回收子View</h3><p>当你觉得一切都非常完美的时候，却忽略了一个很关键的点！那就是回收<del>。我们知道，RecyclerView强大就强大在View的循环回收利用上，而一个View是否需要回收，是由我们的LayoutManager来管理的</del>还记得我们前面说的Remove吗？也就是将View放到Recycle缓存中去~<br>我们前面自定义的LayoutManager并没有回收子View，接下来我们去看看如何循环利用子View吧~。首先，我们应该将所有的item的上下左右的偏移量记录下来，并且要记录哪些Item需要被回收：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//保存所有的Item的上下左右的偏移量信息</span></span><br><span class="line"><span class="keyword">private</span> SparseArray&lt;Rect&gt; allItemFrames = <span class="keyword">new</span> SparseArray&lt;&gt;();</span><br><span class="line"><span class="comment">//记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上，并且还没被回收</span></span><br><span class="line"><span class="keyword">private</span> SparseBooleanArray hasAttachedItems = <span class="keyword">new</span> SparseBooleanArray();</span><br></pre></td></tr></table></figure>

<p>接下来就是初始化这两个变量，在onLayoutChildren()函数中对上面定义的两个变量进行初始化：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onLayoutChildren</span><span class="params">(RecyclerView.Recycler recycler, RecyclerView.State state)</span> </span>&#123;</span><br><span class="line"><span class="comment">//如果没有item，直接返回</span></span><br><span class="line"><span class="keyword">if</span> (getItemCount() &lt;= <span class="number">0</span>) <span class="keyword">return</span>;</span><br><span class="line"><span class="comment">// 跳过preLayout，preLayout主要用于支持动画</span></span><br><span class="line"><span class="keyword">if</span> (state.isPreLayout()) &#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//在布局之前，将所有的子View先Detach掉，放入到Scrap缓存中</span></span><br><span class="line">detachAndScrapAttachedViews(recycler);</span><br><span class="line"><span class="comment">//定义竖直方向的偏移量</span></span><br><span class="line"><span class="keyword">int</span> offsetY = <span class="number">0</span>;</span><br><span class="line">totalHeight = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; getItemCount(); i++) &#123;</span><br><span class="line"></span><br><span class="line"><span class="comment">//这里就是从缓存里面取出</span></span><br><span class="line">View view = recycler.getViewForPosition(i);</span><br><span class="line"><span class="comment">//将View加入到RecyclerView中</span></span><br><span class="line">addView(view);</span><br><span class="line">measureChildWithMargins(view, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">int</span> width = getDecoratedMeasuredWidth(view);</span><br><span class="line"><span class="keyword">int</span> height = getDecoratedMeasuredHeight(view);</span><br><span class="line"></span><br><span class="line">totalHeight += height;</span><br><span class="line">Rect frame = allItemFrames.get(i);</span><br><span class="line"><span class="keyword">if</span> (frame == <span class="keyword">null</span>) &#123;</span><br><span class="line">frame = <span class="keyword">new</span> Rect();</span><br><span class="line">&#125;</span><br><span class="line">frame.set(<span class="number">0</span>, offsetY, width, offsetY + height);</span><br><span class="line"><span class="comment">// 将当前的Item的Rect边界数据保存</span></span><br><span class="line">allItemFrames.put(i, frame);</span><br><span class="line"><span class="comment">// 由于已经调用了detachAndScrapAttachedViews，因此需要将当前的Item设置为未出现过</span></span><br><span class="line">hasAttachedItems.put(i, <span class="keyword">false</span>);</span><br><span class="line"><span class="comment">//将竖直方向偏移量增大height</span></span><br><span class="line">offsetY += height;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//如果所有子View的高度和没有填满RecyclerView的高度，</span></span><br><span class="line"><span class="comment">// 则将高度设置为RecyclerView的高度</span></span><br><span class="line">totalHeight = Math.max(totalHeight, getVerticalSpace());</span><br><span class="line">recycleAndFillItems(recycler, state);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意，上面的for循环里面并没有再调用layoutDecorated()函数，而是在最后调用了recycleAndFillItems()函数，这个函数是先将不需要的Item进行回收，然后在从缓存中取出需要的Item，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 回收不需要的Item，并且将需要显示的Item从缓存中取出</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">recycleAndFillItems</span><span class="params">(RecyclerView.Recycler recycler, RecyclerView.State state)</span> </span>&#123;</span><br><span class="line"><span class="keyword">if</span> (state.isPreLayout()) &#123; <span class="comment">// 跳过preLayout，preLayout主要用于支持动画</span></span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当前scroll offset状态下的显示区域</span></span><br><span class="line">Rect displayFrame = <span class="keyword">new</span> Rect(<span class="number">0</span>, verticalScrollOffset, getHorizontalSpace(), verticalScrollOffset + getVerticalSpace());</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 将滑出屏幕的Items回收到Recycle缓存中</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">Rect childFrame = <span class="keyword">new</span> Rect();</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; getChildCount(); i++) &#123;</span><br><span class="line">View child = getChildAt(i);</span><br><span class="line">childFrame.left = getDecoratedLeft(child);</span><br><span class="line">childFrame.top = getDecoratedTop(child);</span><br><span class="line">childFrame.right = getDecoratedRight(child);</span><br><span class="line">childFrame.bottom = getDecoratedBottom(child);</span><br><span class="line"><span class="comment">//如果Item没有在显示区域，就说明需要回收</span></span><br><span class="line"><span class="keyword">if</span> (!Rect.intersects(displayFrame, childFrame)) &#123;</span><br><span class="line"><span class="comment">//回收掉滑出屏幕的View</span></span><br><span class="line">removeAndRecycleView(child, recycler);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//重新显示需要出现在屏幕的子View</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; getItemCount(); i++) &#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (Rect.intersects(displayFrame, allItemFrames.get(i))) &#123;</span><br><span class="line"></span><br><span class="line">View scrap = recycler.getViewForPosition(i);</span><br><span class="line">measureChildWithMargins(scrap, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">addView(scrap);</span><br><span class="line"></span><br><span class="line">Rect frame = allItemFrames.get(i);</span><br><span class="line"><span class="comment">//将这个item布局出来</span></span><br><span class="line">layoutDecorated(scrap,</span><br><span class="line">frame.left,</span><br><span class="line">frame.top - verticalScrollOffset,</span><br><span class="line">frame.right,</span><br><span class="line">frame.bottom - verticalScrollOffset);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最后不要忘了在scrollVerticallyBy中添加recycleAndFillItems(recycler, state);，因为在滑动过程中，需要重新对Item进行布局，即从缓存中取出Item进行数据绑定后放在新出现的Item的位置上。并且，还需要在scrollVerticallyBy最开始调用detachAndScrapAttachedViews(recycler);代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">scrollVerticallyBy</span><span class="params">(<span class="keyword">int</span> dy, RecyclerView.Recycler recycler, RecyclerView.State state)</span> </span>&#123;</span><br><span class="line"><span class="comment">//先detach掉所有的子View</span></span><br><span class="line">detachAndScrapAttachedViews(recycler);</span><br><span class="line"></span><br><span class="line"><span class="comment">//实际要滑动的距离</span></span><br><span class="line"><span class="keyword">int</span> travel = dy;</span><br><span class="line"></span><br><span class="line"><span class="comment">//如果滑动到最顶部</span></span><br><span class="line"><span class="keyword">if</span> (verticalScrollOffset + dy &lt; <span class="number">0</span>) &#123;</span><br><span class="line">travel = -verticalScrollOffset;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (verticalScrollOffset + dy &gt; totalHeight - getVerticalSpace()) &#123;<span class="comment">//如果滑动到最底部</span></span><br><span class="line">travel = totalHeight - getVerticalSpace() - verticalScrollOffset;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//将竖直方向的偏移量+travel</span></span><br><span class="line">verticalScrollOffset += travel;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 平移容器内的item</span></span><br><span class="line">offsetChildrenVertical(-travel);</span><br><span class="line">recycleAndFillItems(recycler, state);</span><br><span class="line">Log.d(<span class="string">"---&gt;"</span>, <span class="string">" childView count:"</span> + getChildCount());</span><br><span class="line"><span class="keyword">return</span> travel;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://img.vim-cn.com/7b/632d701cd55e3cb8080725e544ac6bfb11c55b.gif" alt="没有效果图"><br>好啦~现在为止我们基本上已经实现了我们想要的效果啦，至于重写onAdapterChanged()以及scrollToPosition()等等</p>

        </div>
        
          


  <section class='meta' id="footer-meta">
    <hr>
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2019-08-26T09:51:18+08:00">
  <a class='notlink'>
    <i class="fas fa-clock" aria-hidden="true"></i>
    <p>最后更新于 2019年8月26日</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/tags/android快速开发/" rel="nofollow"><i class="fas fa-hashtag" aria-hidden="true"></i>&nbsp;<p>android快速开发</p></a></div> <div class="new-meta-item meta-tags"><a class="tag" href="/tags/测试标签/" rel="nofollow"><i class="fas fa-hashtag" aria-hidden="true"></i>&nbsp;<p>测试标签</p></a></div>


        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="QQ好友" rel="external nofollow noopener noreferrer"
          
          href="http://connect.qq.com/widget/shareqq/index.html?url=https://zyqing.ml/2019/08/26/data/layoutManager/&title=打造属于你的LayoutManager | 会飞的猪&summary=打造属于你的LayoutManagerRecyclerView机制RecyclerView内部有个Recycler，它其实就是一个垃圾回收再利用的工具，我们定义LayoutManager时，我们需要将不用的View回收掉；在需要获取新的View时直接申请，即通过getViewForPosition()方法，返回的View可能是之前回收的垃圾View，也可能是new出来的新View，这些都是RecyclerView帮我们做的。那么RecyclerView内部的垃圾View缓存是什么样子的呢？我们接下来看看~"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/assets@19.1.9/logo/128/qq.png">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="QQ空间" rel="external nofollow noopener noreferrer"
          
          href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://zyqing.ml/2019/08/26/data/layoutManager/&title=打造属于你的LayoutManager | 会飞的猪&summary=打造属于你的LayoutManagerRecyclerView机制RecyclerView内部有个Recycler，它其实就是一个垃圾回收再利用的工具，我们定义LayoutManager时，我们需要将不用的View回收掉；在需要获取新的View时直接申请，即通过getViewForPosition()方法，返回的View可能是之前回收的垃圾View，也可能是new出来的新View，这些都是RecyclerView帮我们做的。那么RecyclerView内部的垃圾View缓存是什么样子的呢？我们接下来看看~"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/assets@19.1.9/logo/128/qzone.png">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="微博" rel="external nofollow noopener noreferrer"
          
          href="http://service.weibo.com/share/share.php?url=https://zyqing.ml/2019/08/26/data/layoutManager/&title=打造属于你的LayoutManager | 会飞的猪&summary=打造属于你的LayoutManagerRecyclerView机制RecyclerView内部有个Recycler，它其实就是一个垃圾回收再利用的工具，我们定义LayoutManager时，我们需要将不用的View回收掉；在需要获取新的View时直接申请，即通过getViewForPosition()方法，返回的View可能是之前回收的垃圾View，也可能是new出来的新View，这些都是RecyclerView帮我们做的。那么RecyclerView内部的垃圾View缓存是什么样子的呢？我们接下来看看~"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/xaoxuu/assets@19.1.9/logo/128/weibo.png">
          
        </a>
      
    
  </div>
</div>



        
      
    </div>
  </section>


        
        
            <div class="prev-next">
                
                
                    <section class="next">
                        <span class="art-item-right" aria-hidden="true">
                            <h6>下一页&nbsp;<i class="fas fa-chevron-right" aria-hidden="true"></i></h6>
                            <h4>
                                <a href="/2019/08/26/data/fastAndroid/" rel="prev" title="android快速开发">
                                    
                                        android快速开发
                                    
                                </a>
                            </h4>
                            
                                
                                <h6 class="tags">
                                    <a class="tag" href="/tags/android快速开发/"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i>android快速开发</a> <a class="tag" href="/tags/测试标签/"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i>测试标签</a>
                                </h6>
                            
                        </span>
                    </section>
                
            </div>
        
      </section>
    </article>
  

  
    <!-- 显示推荐文章和评论 -->



  <article class="post white-box comments">
    <section class="article typo">
      <h4><i class="fas fa-comments fa-fw" aria-hidden="true"></i>&nbsp;评论</h4>
      
      
      
      
        <section id="comments">
          <div id="valine_container" class="valine_thread">
            <i class="fas fa-spinner fa-spin fa-fw"></i>
          </div>
        </section>
      
    </section>
  </article>


  




<!-- 根据页面mathjax变量决定是否加载MathJax数学公式js -->



  <script>
    window.subData = {
      title: '打造属于你的LayoutManager',
      tools: true
    }
  </script>


</div>
<aside class='l_side'>
  
    
    
      
        
          
          
            <section class='widget author'>
  <div class='content material'>
    
      <div class='avatar'>
        <img class='avatar' src='http://zy_qing.gitee.io/resources/img/touxiang.jpg'/>
      </div>
    
     
      <div class='text'>
        
        
        
          <p><span id="jinrishici-sentence">柳暗花明又一村</span></p>
          <script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
        
      </div>
    
    
      <div class="social-wrapper">
        
          
            <a href="mailto:1091425078@qq.com"
              class="social fas fa-envelope flat-btn"
              target="_blank"
              rel="external nofollow noopener noreferrer">
            </a>
          
        
          
            <a href="https://github.com/YongQingAndroid"
              class="social fab fa-github flat-btn"
              target="_blank"
              rel="external nofollow noopener noreferrer">
            </a>
          
        
          
            <a href="https://music.163.com/#/user/home?id=343100855"
              class="social fas fa-headphones-alt flat-btn"
              target="_blank"
              rel="external nofollow noopener noreferrer">
            </a>
          
        
      </div>
    
  </div>
</section>

          
        
      
        
          
          
            <section class='widget list'>
  
<header class='material'>
  <div><i class="fas fa-map-signs fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;常用功能</div>
  
</header>

  <div class='content material'>
    <ul class="entry">
      
        <li><a class="flat-box" title="https://dev.tencent.com/u/zyqandroid/p/blog/git" href="https://dev.tencent.com/u/zyqandroid/p/blog/git"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-clock fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;我的GIT
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="/markPage/" href="/markPage/"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-archive fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;本地markDown工具
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="/2md/" href="/2md/"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-book fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;HTML转MD
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="http://zy_qing.gitee.io/resources/" href="http://zy_qing.gitee.io/resources/"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-archive fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;备用markDown工具
          </div>
          
        </a></li>
      
    </ul>
  </div>
</section>

          
        
      
        
          
          
            
  <section class='widget toc-wrapper'>
    
<header class='material'>
  <div><i class="fas fa-list fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;本文目录</div>
  
    <div class='wrapper'><a class="s-toc rightBtn" rel="external nofollow noopener noreferrer" href="javascript:void(0)"><i class="fas fa-thumbtack fa-fw"></i></a></div>
  
</header>

    <div class='content material'>
      <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#打造属于你的LayoutManager"><span class="toc-text">打造属于你的LayoutManager</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#RecyclerView机制"><span class="toc-text">RecyclerView机制</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#RecyclerView的二级缓存"><span class="toc-text">RecyclerView的二级缓存</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#将View缓存的两种方式"><span class="toc-text">将View缓存的两种方式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#开始自定义LayoutManager"><span class="toc-text">开始自定义LayoutManager</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#看一下MainActivity的完整代码吧"><span class="toc-text">看一下MainActivity的完整代码吧~</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#添加滑动"><span class="toc-text">添加滑动</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#回收子View"><span class="toc-text">回收子View</span></a></li></ol></li></ol></li></ol>
    </div>
  </section>


          
        
      
        
          
          
            <section class='widget list'>
  
<header class='material'>
  <div><i class="fas fa-map-signs fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;站内导航</div>
  
</header>

  <div class='content material'>
    <ul class="entry">
      
        <li><a class="flat-box" title="/" href="/"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-clock fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;近期文章
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="/blog/archives/" href="/blog/archives/"
          
            rel="nofollow"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-archive fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;文章归档
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="/projects/" href="/projects/"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-code-branch fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;开源项目
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="/friends/" href="/friends/"
          
            rel="nofollow"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-link fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;我的友链
          </div>
          
        </a></li>
      
        <li><a class="flat-box" title="/about/" href="/about/"
          
            rel="nofollow"
          
          
          >
          <div class='name'>
            
              <i class="fas fa-info-circle fa-fw" aria-hidden="true"></i>
            
            &nbsp;&nbsp;关于我
          </div>
          
        </a></li>
      
    </ul>
  </div>
</section>

          
        
      
        
          
          
            
  <section class='widget category'>
    
<header class='material'>
  <div><i class="fas fa-folder-open fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;文章分类</div>
  
    <a class="rightBtn"
    
      rel="nofollow"
    
    
    href="/blog/categories/"
    title="blog/categories/">
    <i class="fas fa-expand-arrows-alt fa-fw"></i></a>
  
</header>

    <div class='content material'>
      <ul class="entry">
        
          <li><a class="flat-box" title="/categories/Android/" href="/categories/Android/"><div class='name'>Android</div><div class='badge'>(6)</div></a></li>
        
          <li><a class="flat-box" title="/categories/教程/" href="/categories/教程/"><div class='name'>教程</div><div class='badge'>(1)</div></a></li>
        
      </ul>
    </div>
  </section>


          
        
      
        
          
          
            
  <section class='widget tagcloud'>
    
<header class='material'>
  <div><i class="fas fa-fire fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;热门标签</div>
  
    <a class="rightBtn"
    
      rel="nofollow"
    
    
    href="/blog/tags/"
    title="blog/tags/">
    <i class="fas fa-expand-arrows-alt fa-fw"></i></a>
  
</header>

    <div class='content material'>
      <a href="/tags/android/" style="font-size: 14px; color: #999">android</a> <a href="/tags/android快速开发/" style="font-size: 24px; color: #555">android快速开发</a> <a href="/tags/hook/" style="font-size: 19px; color: #777">hook</a> <a href="/tags/rxjava/" style="font-size: 14px; color: #999">rxjava</a> <a href="/tags/测试标签/" style="font-size: 19px; color: #777">测试标签</a>
    </div>
  </section>


          
        
      
        
          
          
            


  <section class='widget music'>
    
<header class='material'>
  <div><i class="fas fa-compact-disc fa-fw" aria-hidden="true"></i>&nbsp;&nbsp;最近在听</div>
  
    <a class="rightBtn"
    
      rel="external nofollow noopener noreferrer"
    
    
      target="_blank"
    
    href="https://music.163.com/#/user/home?id=343100855"
    title="https://music.163.com/#/user/home?id=343100855">
    <i class="far fa-heart fa-fw"></i></a>
  
</header>

    <div class='content material'>
      
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer@1.7.0/dist/APlayer.min.css">
  <div class="aplayer"
    data-theme="#1BCDFC"
    
    
    data-mode="circulation"
    data-server="netease"
    data-type="playlist"
    data-id="481100619"
    data-volume="0.7">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/aplayer@1.7.0/dist/APlayer.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/meting@1.1.0/dist/Meting.min.js"></script>


    </div>
  </section>


          
        
      
    

  
</aside>

<footer id="footer" class="clearfix">
  
  
    <div class="social-wrapper">
      
        
          <a href="mailto:1091425078@qq.com"
            class="social fas fa-envelope flat-btn"
            target="_blank"
            rel="external nofollow noopener noreferrer">
          </a>
        
      
        
          <a href="https://github.com/YongQingAndroid"
            class="social fab fa-github flat-btn"
            target="_blank"
            rel="external nofollow noopener noreferrer">
          </a>
        
      
        
          <a href="https://music.163.com/#/user/home?id=343100855"
            class="social fas fa-headphones-alt flat-btn"
            target="_blank"
            rel="external nofollow noopener noreferrer">
          </a>
        
      
    </div>
  
  <br>
  <div><p>博客内容遵循 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</a></p>
</div>
  <div>
    本站使用
    <a href="https://xaoxuu.com/wiki/material-x/" target="_blank" class="codename">Material X</a>
    作为主题
    
      ，
      总访问量为
      <span id="busuanzi_value_site_pv"><i class="fas fa-spinner fa-spin fa-fw" aria-hidden="true"></i></span>
      次
    
    。
  </div>
  <!-- 血小板 -->
  <div id="landlord">
      <div class="message" style="opacity:0"></div>
      <canvas id="live2d" width="560" height="500" class="live2d"></canvas>
      <div class="hide-button">隐藏</div>
  </div>
</footer>
<script>setLoadingBarProgress(80);</script>
<!-- live2d 血小板 -->
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript">
    var message_Path = '/live2d/'
    var home_Path = 'https://haremu.com/'
</script>
<script type="text/javascript" src="/live2d/js/live2d.js"></script>
<script type="text/javascript" src="/live2d/js/message.js"></script>
<script type="text/javascript">
    loadlive2d("live2d", "/live2d/model/xiaoban/model.json");
</script>


      <script>setLoadingBarProgress(60);</script>
    </div>
    <a class="s-top fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>

  <script>
    var GOOGLE_CUSTOM_SEARCH_API_KEY = "";
    var GOOGLE_CUSTOM_SEARCH_ENGINE_ID = "";
    var ALGOLIA_API_KEY = "";
    var ALGOLIA_APP_ID = "";
    var ALGOLIA_INDEX_NAME = "";
    var AZURE_SERVICE_NAME = "";
    var AZURE_INDEX_NAME = "";
    var AZURE_QUERY_KEY = "";
    var BAIDU_API_ID = "";
    var SEARCH_SERVICE = "hexo" || "hexo";
    var ROOT = "/"||"/";
    if(!ROOT.endsWith('/'))ROOT += '/';
  </script>

<script src="//instant.page/1.2.2" type="module" integrity="sha384-2xV8M5griQmzyiY3CDqh1dn4z3llDVqZDqzjzcY+jCBCk/a5fXJmuZ/40JJAPeoU"></script>


  <script async src="https://cdn.jsdelivr.net/npm/scrollreveal@4.0.5/dist/scrollreveal.min.js"></script>
  <script type="text/javascript">
    $(function() {
      const $reveal = $('.reveal');
      if ($reveal.length === 0) return;
      const sr = ScrollReveal({ distance: 0 });
      sr.reveal('.reveal');
    });
  </script>


  <script src="https://cdn.jsdelivr.net/npm/node-waves@0.7.6/dist/waves.min.js"></script>
  <script type="text/javascript">
    $(function() {
      Waves.attach('.flat-btn', ['waves-button']);
      Waves.attach('.float-btn', ['waves-button', 'waves-float']);
      Waves.attach('.float-btn-light', ['waves-button', 'waves-float', 'waves-light']);
      Waves.attach('.flat-box', ['waves-block']);
      Waves.attach('.float-box', ['waves-block', 'waves-float']);
      Waves.attach('.waves-image');
      Waves.init();
    });
  </script>


  <script async src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js"></script>












  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  
    
      <script src="https://cdn.jsdelivr.net/gh/xaoxuu/volantis@1.0/js/volantis.min.js"></script>
    
  
  <script>
  var GUEST_INFO = ['nick','mail','link'];
  var guest_info = 'nick,mail,link'.split(',').filter(function(item){
    return GUEST_INFO.indexOf(item) > -1
  });
  var notify = 'true' == true;
  var verify = 'true' == true;
  var valine = new Valine();
  valine.init({
    el: '#valine_container',
    notify: notify,
    verify: verify,
    guest_info: guest_info,
    
    appId: "nbvwRDSIj9302hYN0p3EgCgT-gzGzoHsz",
    appKey: "XVOUM8qKVsqegg2TnN7sHpMQ",
    placeholder: "快来评论吧~",
    pageSize:'10',
    avatar:'mp',
    lang:'zh-cn',
    highlight:'true'
  })
  </script>



  <script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-material-x@19.5/js/app.js"></script>


  <script src="https://cdn.jsdelivr.net/gh/xaoxuu/cdn-material-x@19.5/js/search.js"></script>




<!-- 复制 -->
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  let COPY_SUCCESS = "复制成功";
  let COPY_FAILURE = "复制失败";
  /*页面载入完成后，创建复制按钮*/
  !function (e, t, a) {
    /* code */
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '  <i class="fa fa-copy"></i><span>复制</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });

      clipboard.on('success', function(e) {
        //您可以加入成功提示
        console.info('Action:', e.action);
        console.info('Text:', e.text);
        console.info('Trigger:', e.trigger);
        success_prompt(COPY_SUCCESS);
        e.clearSelection();
      });
      clipboard.on('error', function(e) {
        //您可以加入失败提示
        console.error('Action:', e.action);
        console.error('Trigger:', e.trigger);
        fail_prompt(COPY_FAILURE);
      });
    }
    initCopyCode();

  }(window, document);

  /**
   * 弹出式提示框，默认1.5秒自动消失
   * @param message 提示信息
   * @param style 提示样式，有alert-success、alert-danger、alert-warning、alert-info
   * @param time 消失时间
   */
  var prompt = function (message, style, time)
  {
      style = (style === undefined) ? 'alert-success' : style;
      time = (time === undefined) ? 1500 : time*1000;
      $('<div>')
          .appendTo('body')
          .addClass('alert ' + style)
          .html(message)
          .show()
          .delay(time)
          .fadeOut();
  };

  // 成功提示
  var success_prompt = function(message, time)
  {
      prompt(message, 'alert-success', time);
  };

  // 失败提示
  var fail_prompt = function(message, time)
  {
      prompt(message, 'alert-danger', time);
  };

  // 提醒
  var warning_prompt = function(message, time)
  {
      prompt(message, 'alert-warning', time);
  };

  // 信息提示
  var info_prompt = function(message, time)
  {
      prompt(message, 'alert-info', time);
  };

</script>


<!-- fancybox -->
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<script>
  let LAZY_LOAD_IMAGE = "";
  $(".article-entry").find("fancybox").find("img").each(function () {
      var element = document.createElement("a");
      $(element).attr("data-fancybox", "gallery");
      $(element).attr("href", $(this).attr("src"));
      /* 图片采用懒加载处理时,
       * 一般图片标签内会有个属性名来存放图片的真实地址，比如 data-original,
       * 那么此处将原本的属性名src替换为对应属性名data-original,
       * 修改如下
       */
       if (LAZY_LOAD_IMAGE) {
         $(element).attr("href", $(this).attr("data-original"));
       }
      $(this).wrap(element);
  });
</script>





  <script>setLoadingBarProgress(100);</script>

</body>
</html>
<!--单击显示文字-->
<script type="text/javascript" src="/js/click_show_text.js"></script>

